#include "circular_buffer.h"
#include "debug_log.h"
#include <stdio.h>

circular_buffer_t* init_circular_buffer(size_t size)
{
	circular_buffer_t* circular_buf = (circular_buffer_t*)malloc(sizeof(circular_buffer_t) + size*2);
	dbg_assert(circular_buf != NULL);

	circular_buf->buf = (char *)circular_buf + sizeof(circular_buffer_t);
	circular_buf->max = size;
	circular_buf->base = 0;
	circular_buf->top = 0;
	circular_buf->q = 0;
	circular_buf->count = 0;

	return circular_buf;
}

int write_to_circular_buffer(circular_buffer_t* circular_buf,char *s,size_t l)
{
	if (circular_buf->q + l > circular_buf->max)
	{
		return -1; // overflow
	}
	circular_buf->count += (unsigned long)l;
	if (circular_buf->top + l > circular_buf->max) // block crosses circular border
	{
		size_t l1 = circular_buf->max - circular_buf->top; // size left until circular border crossing
		// always copy full block to buffer(circular_buf->buf) + top pointer(circular_buf->top)
		// because we have doubled the buffer size for performance reasons
		memcpy(circular_buf->buf + circular_buf->top, s, l);
		memcpy(circular_buf->buf, s + l1, l - l1);
		circular_buf->top = l - l1;
		circular_buf->q += l;
	}
	else
	{
		memcpy(circular_buf->buf + circular_buf->top, s, l);
		memcpy(circular_buf->buf + circular_buf->max + circular_buf->top, s, l);
		circular_buf->top += l;
		if (circular_buf->top >= circular_buf->max)
			circular_buf->top -= circular_buf->max;
		circular_buf->q += l;
	}
	return 0;
}

int read_from_circular_buffer(circular_buffer_t* circular_buf,char *s,size_t l)
{
	if (l > circular_buf->q)
	{
		return -1; // not enough chars
	}

	if (circular_buf->base + l > circular_buf->max) // block crosses circular border
	{
		size_t l1 = circular_buf->max - circular_buf->base;
		if (s)
		{
			memcpy(s, circular_buf->buf + circular_buf->base, l1);
			memcpy(s + l1, circular_buf->buf, l - l1);
		}
		circular_buf->base = l - l1;
		circular_buf->q -= l;
	}
	else
	{
		if (s)
		{
			memcpy(s, circular_buf->buf + circular_buf->base, l);
		}
		circular_buf->base += l;
		if (circular_buf->base >= circular_buf->max)
			circular_buf->base -= circular_buf->max;
		circular_buf->q -= l;
	}
	if (!circular_buf->q)
	{
		circular_buf->base = circular_buf->top = 0;
	}
	return 0;
}

int remove_from_circular_biffer(circular_buffer_t* circular_buf,size_t l)
{
	return read_from_circular_buffer(circular_buf,NULL,l);
}

int length_of_cicular_buffer(circular_buffer_t* circular_buf)
{
	return circular_buf->q;
}

char* get_start_of_circular_buffer(circular_buffer_t* circular_buf)
{
	return circular_buf->buf + circular_buf->base;
}

int space_of_circular_buffer(circular_buffer_t* circular_buf)
{
	return circular_buf->max - circular_buf->q;
}

void dispose_circular_buffer(circular_buffer_t* circular_buf)
{
	free(circular_buf);
}